import { Inject } from '../decorates/inject';
import { Draw } from './draw';
import { Config } from './config';
import { SELFEVENT } from '../interfaces/event';
import { fromEvent, of, from, timer, Observable, zip, Subject } from 'rxjs';
import { map, buffer, debounceTime, takeUntil, concatAll, mergeMap, debounce, combineAll} from 'rxjs/operators';
import { numToCharator } from '../utils/transate';
import { IZCustomEvent } from '../interfaces/event';
import { MulticastOperator } from 'rxjs/internal/operators/multicast';
// import * as fromPromise from 'rxjs/observable/fromPromise';
// console.log(fromPromise)
@Inject()
export class Events {
    protected mousedown: Observable<MouseEvent>;
    protected mouseup: Observable<MouseEvent>;
    protected mousemove: Observable<MouseEvent>;
    public wheelEvt: Subject<CustomEvent<{speed: number}>> = new Subject();
    public clickEvt: Subject<CustomEvent<IZCustomEvent>> = new Subject();
    public dbclickEvt: Subject<CustomEvent<IZCustomEvent>> = new Subject();
    public dragStartEvt: Subject<CustomEvent<IZCustomEvent>> = new Subject();
    public dragMoveEvt: Subject<CustomEvent<IZCustomEvent>> = new Subject();
    public dragEndEvt: Subject<CustomEvent<IZCustomEvent>> = new Subject();
    public windowResizeEvt: Subject<CustomEvent<IZCustomEvent>> = new Subject();
    constructor(protected draw: Draw,protected conf: Config) {
        this.draw.sub.subscribe(res => {
            if (res == 'afterDomInit') {
                this.bindEvent();
            }
        });
    }
    public bindEvent() {
        let ele = this.draw.container;
        this.mousedown = fromEvent<MouseEvent>(ele, 'mousedown');
        this.mouseup = fromEvent<MouseEvent>(ele, 'mouseup');
        this.mousemove = fromEvent<MouseEvent>(ele, 'mousemove');
        fromEvent<WheelEvent>(ele, 'mousewheel').pipe(map(val => {
            return val;
        }))
        .subscribe(ev => {
            let Event = document.createEvent('CustomEvent');
            if(ev.deltaY > 0) {
                Event.initCustomEvent(name, true,false, { speed: 1});  
                this.conf.startRow += 1;
            }else{
                Event.initCustomEvent(name, true,false, { speed: -1}); 
                this.conf.startRow -= 1;
            }
            this.wheelEvt.next(Event);
        })
        this.bindScrollEvent();
        this.eventTest(ele);
        this.spliteClickEvent();
        this.eventMove();
        this.bindResize();
    }
    protected bindScrollEvent() {
        let ele = this.draw.container;
        ele.addEventListener('scroll', (ev) => {
            console.log(ev)
        })
        console.log()
    }
    protected eventTest(ele: HTMLElement) {

    }
    protected bindResize() {
        let MutationObserver = (<any>window).MutationObserver || (<any>window).WebKitMutationObserver || (<any>window).MozMutationObserver;
        console.log(MutationObserver)
        new MutationObserver((data: any) => {
            console.log('-----------')
            console.log(data)
        });
        window.addEventListener('resize', () => {
            this.windowResizeEvt.next();
        })
    }
    protected eventMove() {
        let startEvt: MouseEvent;
        let isStart: boolean = false;
        let test = this.mousedown.pipe(
            map(downEv => {
                startEvt =  downEv;
                this.dragStartEvt.next(this.createCustomEvent(downEv,'dragStartEvt'))
                return this.mousemove.pipe(takeUntil(this.mouseup.pipe(
                    map(msUp => {
                        if(msUp.timeStamp - startEvt.timeStamp > 300) {
                            isStart = false;  
                            startEvt = <any>undefined;
                            this.dragEndEvt.next(this.createCustomEvent(msUp, 'dragEndEvt'));
                        }
                        return msUp;
                    })
                ))).pipe(map(mvEvt => {
                    if(mvEvt.timeStamp - startEvt.timeStamp > 300 || (Math.abs(mvEvt.clientX - startEvt.clientX) > 20 || Math.abs(mvEvt.clientY - startEvt.clientY) > 20)) {
                        if(isStart == false) {
                            this.dragStartEvt.next(this.createCustomEvent(startEvt,'dragStartEvt'));
                            isStart = true;
                        }else{
                           this.dragMoveEvt.next(this.createCustomEvent(mvEvt, 'dragMoveEvt')); 
                        } 
                    }
                    return mvEvt;
                }));
            }),
            concatAll()
        );
        test.subscribe(res => {
            // console.log('end')
        }, (err) => {

        }, () => {
            console.log('complete')
        });
    }
    protected spliteClickEvent() {
        let mouse$ = this.mousedown
        .pipe(
            map((ev) => {
                return from(new Promise(resolve => {
                    this.mouseup.subscribe(res => {
                        resolve([ev,res])
                    });
                }))
            }),
            concatAll()
        );
        const buff$ = 
        mouse$
        .pipe(
            debounceTime(250),
        )
        mouse$.pipe(
            buffer(buff$),
            map(list => {
                return list;
            })
        )
        .subscribe((res: any) => {
            try {
                let evs = res[res.length - 1];
                let time = Math.abs(evs[0].timeStamp - evs[1].timeStamp);
                if(time > 300) return;
                
                if(res.length == 1) {
                    // console.log('click')
                    this.clickEvt.next(this.createCustomEvent(evs[1], 'click'))
                    this.click(evs[1]);
                }else{
                    //console.log('dbclick');
                    // console.log('dbclick')
                    this.dbclickEvt.next(this.createCustomEvent(evs[1], 'dbclick'))
                    // this.dbclick(evs[1]);
                }
            } catch (error) {
                
            }
        });
    }
    protected click(ev: MouseEvent) {
        let startCell = {
            row: this.conf.startRow,
            col: this.conf.startCol
        };
        let endCell = {
            row: this.conf.visualMaxRow,
            col: this.conf.visualMaxCol
        }
        let clickPosition = {
            row: -1,
            col: -1
        }
        let width = this.conf.colHeaderWidth + 1;
        let drawPoint = {
            x: 0,
            y: 0
        }
        for(let i = startCell.col; i < endCell.col + 1; i++) {
            let newWidth = width + this.conf.getColWidth(i) + 1;
            if(ev.offsetX < newWidth && ev.offsetX > width) {
                drawPoint.x = width;
                clickPosition.col = i;
                break;
            }
            width = newWidth;
        }
        let height = this.conf.rowHeaderHeight + 1;
        for(let i = startCell.row; i < endCell.row; i++) {
            let newHeight =height + this.conf.getRowHeight(i) + 1;
            if(ev.offsetY < newHeight && ev.offsetY > height) {
                drawPoint.y = height;
                clickPosition.row = i;
                break;
            }
            height = newHeight;
        }
        let Event = document.createEvent('CustomEvent');
        if(ev.offsetX < this.conf.colHeaderWidth && ev.offsetY < this.conf.rowHeaderHeight) {
            console.log('corner');
            return ;
        }
        if(ev.offsetX < this.conf.colHeaderWidth) {
            console.log('row header')
            return ;
        }
        if(ev.offsetY < this.conf.rowHeaderHeight) {
            console.log('col header')
            return ;
        }
        let cell = this.conf.getCell(clickPosition.row, clickPosition.col);
        cell = this.conf.getCell(cell.merge.row, cell.merge.col);
        Event.initCustomEvent('zClick', true,false, {
            originEvent: ev, 
            row: clickPosition.row, 
            col: clickPosition.col, 
            cell: cell,
            cellPosition: {
                x: drawPoint.x,
                y: drawPoint.y
            }
        });
        this.draw.container.dispatchEvent(Event);
    }
    protected cellClick(ev: MouseEvent) {
        // let startCell = {
        //     row: this.conf.startRow,
        //     col: this.conf.startCol
        // };
        // let endCell = {
        //     row: this.conf.visualMaxRow,
        //     col: this.conf.visualMaxCol
        // }
        // let startWidth = this.conf.colHeaderWidth + 1;
        // for(let i = startCell.col; i < endCell.col; i++) {
            
        // }
    }
    protected dbclick(ev: Event) {
        console.log('dbclick')
    }
    /**
     * 
     * @param originEvent MouseEvent
     * @param name string
     * @param canBubbleArg default true
     * @param cancelableArg default false
     */
    private createCustomEvent(originEvent: MouseEvent, name: string, canBubbleArg: boolean = true, cancelableArg: boolean = false): CustomEvent<IZCustomEvent> {
        let Event = document.createEvent('CustomEvent');
        let startCell = {
            row: this.conf.startRow,
            col: this.conf.startCol
        };
        let endCell = {
            row: this.conf.visualMaxRow,
            col: this.conf.visualMaxCol
        }
        let clickPosition = {
            row: -1,
            col: -1
        }
        let width = this.conf.colHeaderWidth + 1;
        let drawPoint = {
            x: 0,
            y: 0
        }
        for(let i = startCell.col; i < endCell.col; i++) {
            let newWidth = width + this.conf.getColWidth(i) + 1;
            if(originEvent.offsetX <= newWidth && originEvent.offsetX >= width) {
                drawPoint.x = width;
                clickPosition.col = i;
                break;
            }
            width = newWidth;
        }
        let height = this.conf.rowHeaderHeight + 1;
        for(let i = startCell.row; i < endCell.row; i++) {
            let newHeight =height + this.conf.getRowHeight(i) + 1;
            if(originEvent.offsetY <= newHeight && originEvent.offsetY >= height) {
                drawPoint.y = height;
                clickPosition.row = i;
                break;
            }
            height = newHeight;
        }
        let cell = this.conf.getCell(clickPosition.row, clickPosition.col);
        cell = this.conf.getCell(cell.merge.row, cell.merge.col);
        Event.initCustomEvent(name, canBubbleArg,cancelableArg, {
            originEvent: originEvent, 
            row: clickPosition.row, 
            col: clickPosition.col, 
            cell: cell,
            cellPosition: {
                x: drawPoint.x,
                y: drawPoint.y
            }
        });
        return Event;
    }
}
